package net.projecteuler.programs;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Stack;

public final class TimeLogger {
	
	private static final int GAP = 2;
	
	private static ThreadLocal<LinkedHashMap<String, Stack<Long>>> local = new ThreadLocal<LinkedHashMap<String, Stack<Long>>>();
	private static ThreadLocal<List<String>> keylocal = new ThreadLocal<List<String>>();

	public static boolean debug = false;

	public static void tag() {
		LinkedHashMap<String, Stack<Long>> map = getCurMap();
		String key = getKey();
		Stack<Long> stack = map.get(key);
		if(stack == null){
			stack = new Stack<Long>();
			refreshKey(key, true);
			map.put(key, stack);
		}
		
		stack.add(System.currentTimeMillis());
	}
	
	public static void print(){
		print(null);
	}
	
	public static void print(String msg){
		long t2 = System.currentTimeMillis();
		
		LinkedHashMap<String, Stack<Long>> map = getCurMap();
		if(!map.isEmpty()){
			String key = getKey();
			refreshKey(key, false);
			Stack<Long> stack = map.get(key);
			if(!stack.isEmpty()){
				long t1 = stack.pop();
				if(msg == null || msg.trim().equals("")){
					msg = key.substring(key.lastIndexOf("->") + 2);
				}
				
				log("===============================================================================");
				log(msg + " cost time : " + (t2 - t1)*1.0d + "ms");
				if(debug){
					log("method stack: " + key);
				}
				log("===============================================================================");
			}
		}
	}
	
	private static void refreshKey(String key, boolean addKey) {
		List<String> keyList = keylocal.get();
		String lastKey = null;
		if(keyList == null){
			keyList = new ArrayList<String>();
			keylocal.set(keyList);
			
		}else {
			int index = keyList.size() - 1;
			if(index != -1){
				lastKey = keyList.get(index);
				while(!key.startsWith(lastKey)){
					keyList.remove(index);
					local.get().remove(lastKey);
//					log("remove key :" + lastKey + ", last size : " + keyList.size());
					
					index--;
					if(index != -1){
						lastKey = keyList.get(index);
					}else {
						lastKey = "";
					}
				}
			}
		}
		
		if(addKey && !key.equals(lastKey)){
			keyList.add(key);
		}
	}
	
	private static LinkedHashMap<String, Stack<Long>> getCurMap(){
		LinkedHashMap<String, Stack<Long>> map = local.get();
		if(map == null){
			map = new LinkedHashMap<String, Stack<Long>>();
			local.set(map);
		}
		return map;
	}
	
	private static String getKey() {
		StackTraceElement[] els = Thread.currentThread().getStackTrace();
		StringBuffer key = new StringBuffer(els.length);
		int lastMethod = GAP + 1;
		for (int i = els.length - 1; i > GAP; i--) {
			StackTraceElement el = els[i];
			if(el.getClassName().equals(TimeLogger.class.getName())){
				key.setLength(key.length() - 2);
				break;
			}
			key.append(el.getClassName()).append(".").append(el.getMethodName());
			if(i > lastMethod){
				key.append("->");
			}
		}
		return key.toString();
	}
	
	private static void log(String msg){
		System.out.println(msg);
	}
	
}
